package org.mashart.pink.m;

import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

import org.mashart.pink.m.gps.InternalGpsConnector;
import org.mashart.pink.m.gps.BtConnector;
import org.mashart.pink.m.gps.DeviceConnector;
import org.mashart.pink.m.gps.Receiver;
import org.mashart.pink.m.store.Preferences;
import org.mashart.pink.m.gui.Gui;
import org.mashart.pink.m.gui.PreferencesGUI;

/**
 * App main class that implements standard MIDlet methods, like
 * <code>startApp()</code>, <code>destroyApp()</code> and
 * <code>pauseApp()</code>
 */
public class PinkrMIDlet extends MIDlet {

    /**
     * Main class instance for user interface.
     * Initialized in <code>startApp()</code>
     */
    private Gui gui;
    /**
     * User interface for setting preferences
     */
    private PreferencesGUI prefGui;
    private InternalGpsConnector internalConnector;
    private BtConnector btConnector;
    /**
     * GPS connector in use
     */
    private DeviceConnector conn;
    /**
     * Instance of a class that collects data from a GPS device.
     * Running in a separate thread.
     * Initialized in <code>startApp()</code>
     */
    private Receiver recv;
    private boolean initialized;
    /**
     * true when application is paused
     * false otherwise
     */
    private boolean isPaused;
    /**
     * true when a round is started
     * false when the round is stopped
     */
    private boolean roundStarted;
    /**
     * internal GPS device type
     */
    public static final int INTERNAL_DEVICE_TYPE = 0;
    /**
     * bluetooth GPS device type
     */
    public static final int BT_DEVICE_TYPE = 1;

    /**
     * Constructor. Doesn't take any arguments
     */
    public PinkrMIDlet() {
        isPaused = false;
        roundStarted = false;
    }

    /**
     * Get the GUI
     */
    public Gui getGui() {
        return gui;
    }

    /**
     * Called on the application execution termination.
     * Should close all open connections (Bluetooth & HTTP to the server)
     */
    public void destroyApp(boolean unconditional)
            throws MIDletStateChangeException {
        // stop the receiver first
        if (initialized) {
            recv.terminate();
        }
    }

    /**
     * called when app has to be suspended
     * user's intention or mobile device
     */
    public void pauseApp() {
        isPaused = true;
    /* TODO how do we keep running in background? Reply: Done by hiding window*/
    }

    /**
     * Main method, i.e. starting point for the application.
     * Initializes UI, GPS data receiver and network/file connector (for sending/saving the data)
     *
     * Also, shows splash screen during initialization
     */
    public void startApp() throws MIDletStateChangeException {
        if (isPaused) {
            isPaused = false;
            /* TODO why return? */
            return;
        }
        /* init the GUI and show splash screen as the first thing */
        gui = new Gui();
        gui.setMidlet(this);
        gui.showSplash();

        setPreferences(false);
    }

    /**
     * Set the preferences of the application.
     * @param forced if false, do not edit the preference if already set, if true, edit the preference regardless whether already set.
     */
    public void setPreferences(boolean forced) {
        // stop the receiver first, if initialized
        if (initialized) {
            recv.terminate();
            recv = null;
        }
        if (prefGui == null) {
            prefGui = new PreferencesGUI();
        }
        prefGui.setMidlet(this);
        prefGui.showPreferencesEditor(forced);
    }

    /**
     * Initialize
     */
    public void initialize() {

        /* instantiate GPS data receiver class */
        recv = new Receiver();

        recv.setDeviceConnector(conn);
        gui.setReceiver(recv);

        /* Initialize GPS data receiver instance.
         * That includes search for Bluetooth devices too */
        recv.configure();

        if (recv.initialize() < 1) {
            initialized = false;
            /* TODO more helpful error messages */
            prefGui.showInitializationError();
        } else {
            /**
             * TODO we have a debug screen only for the moment
             *      extend this into a fancy UI */
            initialized = true;
            prefGui.showReadyConsole();
        }
    }

    /**
     * Discover available GPS devices
     */
    public void discoverDeviceTypes() {
        prefGui.showDeviceTypesConsole(InternalGpsConnector.isAPIAvailable(), BtConnector.isAPIAvailable());
    }

    /**
     * Set GPS device type, e.g, built-in GPS or bluetooth GPS.
     */
    public void setDeviceType(int type) {
        if (type == INTERNAL_DEVICE_TYPE) {
            internalConnector = new InternalGpsConnector();
            internalConnector.configure();
            internalConnector.initialize();
            conn = internalConnector;
            initialize();
        } else if (type == BT_DEVICE_TYPE) {
            btConnector = new BtConnector();
            btConnector.configure();
            btConnector.discoverRemoteDevices();
            conn = btConnector;
            prefGui.showBtDevicesConsole(btConnector.getRemoteDeviceNames());
        }
    }

    /**
     * Set GPS device, e.g, built-in GPS or URL of the specified bluetooth GPS
     */
    public void setDevice(String type, String url) {
        if (type.equals(Preferences.vBuiltin)) {
            internalConnector = new InternalGpsConnector();
            internalConnector.configure();
            conn = internalConnector;
        } else if (type.equals(Preferences.vBluetooth)) {
            btConnector = new BtConnector();
            btConnector.configure();
            btConnector.setBtConnectoinURL(url);
            conn = btConnector;
        }
        initialize();
    }

    /**
     * Set a bluetooth GPS device connector
     */
    public void setBtRemoteDevice(int remoteDeviceIndex) {
        ((BtConnector) conn).setRemoteDevice(remoteDeviceIndex);
        initialize();
    }

    /**
     * Get bluetooth Connection URL
     */
    public String getBtConnectionURL() {
        return ((BtConnector) conn).getBtConnectoinURL();
    }

    /**
     * Set bluetooth Connection URL
     */
    public void setBtConnectionURL(String url) {
        ((BtConnector) conn).setBtConnectoinURL(url);
    }

    /**
     * Starts the GPS device
     */
    public void startRound() {
        // if already running
        if (roundStarted) {
            return;
        }
        recv.startRound();
        roundStarted = true;
//        gui.showStartedConsole();
    }

    /**
     * Pause a round
     */
    public void pauseRound() {
        recv.pauseRound();
    }

    /**
     * Resume a round
     */
    public void resumeRound() {
        recv.resumeRound();
    }

    /**
     * Stops the GPS device
     */
    public void stopRound() {
        // if already stopped
        if (!roundStarted) {
            return;
        }
        recv.stopRound();
        roundStarted = false;
//        gui.showStoppedConsole();
    }

    /**
     * Terminate
     */
    public void terminate() {
        if (roundStarted) {
            stopRound();
        }
        if (recv != null) {
            recv.terminate();
            recv = null;
        }
        initialized = false;
    }
}
